import Bowser from 'bowser';
import uaParser from 'ua-parser-js';
import { ReportParams } from './types';

class Utils {
	lastEvent?: Event;
	isFlushPending: boolean;
	isBeforeunload: boolean;
	constructor() {
		this.isFlushPending = false;
		this.isBeforeunload = false;
		this.setupEventListeners();
		this.rewriteHistoryMethods();
		this.beforeunloadFix();
	}
	// 初始化事件监听器
	private setupEventListeners() {
		document.addEventListener('click', this.handleClickEvent.bind(this), {
			capture: true,
			passive: true,
		});
	}

	// 处理点击事件
	private handleClickEvent(event: Event) {
		// 最后一次事件源存储
		this.lastEvent = event;
	}

	// 获取选择器链
	getSelectorChain() {
		const target = this.lastEvent?.target as Element;
		const chain = [];
		let parentNode: Element | null = null;
		if (target) {
			chain.push(target);
			parentNode = target?.parentNode as Element;
		}
		while (parentNode) {
			if (parentNode.tagName.toLocaleUpperCase() === 'BODY') {
				return chain;
			}
			chain.push(parentNode);
			parentNode = parentNode.parentNode as Element;
		}
		return chain;
	}

	// 获取选择器
	getSelectors(path: Array<any> = []) {
		const selectorChain: Element[] =
			Array.isArray(path) && path.length > 0 ? path : this.getSelectorChain();
		return selectorChain
			.reverse()
			.map(element => {
				if (element.id) {
					return encodeURIComponent(
						`${element.nodeName.toLowerCase()}#${element.id}`
					);
				}
				if (element.className && typeof element.className === 'string') {
					return `${element.nodeName.toLowerCase()}.${element.className}`;
				}
				return element.nodeName.toLowerCase();
			})
			.join(' > ');
	}

	log(...args: unknown[]) {
		// 颜色配置
		const STYLES = {
			info: 'color: #1890ff; font-size: 14px;', // 蓝色
			warn: 'color: #faad14; font-size: 14px;', // 橙色
			error: 'color: #f5222d; font-size: 14px;', // 红色
			debug: 'color: #52c41a; font-size: 14px;', // 绿色
			default: 'color: #666; font-size: 14px;', // 灰色
		}; // 识别日志类型

		const validTypes = ['info', 'warn', 'error', 'debug'];
		let type = 'info';
		let messages: unknown[] = []; // 智能参数解析

		if (
			args.length > 0 &&
			typeof args[0] === 'string' &&
			validTypes.includes(args[0])
		) {
			type = args[0];
			messages = args.slice(1);
		} else {
			messages = args;
		} // 构建控制台参数

		const labelStyle = STYLES[type as keyof typeof STYLES] || STYLES.default;
		let formatStr = `%c[monitor-sdk-${type}]:`;
		const consoleArgs: unknown[] = [labelStyle]; // 处理每条消息

		messages.forEach(msg => {
			const isObject = msg !== null && typeof msg === 'object';
			formatStr += `%c ${isObject ? '%o' : '%s'}`;
			consoleArgs.push(labelStyle, msg);
		}); // 选择对应的console方法

		const logger =
			{
				info: console.info,
				warn: console.warn,
				error: console.error,
				debug: console.debug,
			}[type] || console.log;

		logger(formatStr, ...consoleArgs);
	}

	// 生成hashCode
	generateUniqueHash(params: { [key: string]: any }): string | null {
		// 类型判断辅助函数
		const getType = (obj: any) => {
			if (obj === null) return 'null';
			if (Array.isArray(obj)) return 'array';
			return typeof obj;
		};

		// 稳定字符串化函数（处理键顺序和特殊类型）
		const stableStringify = (obj: any) => {
			const type = getType(obj);

			switch (type) {
				case 'null':
					return 'null';
				case 'boolean':
				case 'number':
					return obj.toString();
				case 'string':
					return `"${obj.replace(/"/g, '\\"')}"`;
				case 'array':
					return `[${obj.map((item: any) => stableStringify(item)).join(',')}]`;
				case 'object':
					// eslint-disable-next-line no-case-declarations
					const keyValuePairs = newFunction();
					return `{${keyValuePairs.join(',')}}`;
				default:
					throw new Error(`Unsupported data type: ${type}`);
			}

			function newFunction(): string[] {
				const sortedKeys = Object.keys(obj).sort();
				const keyValuePairs = sortedKeys.map(key => {
					return `"${key}":${stableStringify(obj[key])}`;
				});
				return keyValuePairs;
			}
		};

		// 高效哈希生成函数
		const generateHash = (str: string) => {
			let hash = 0;
			const len = str.length;

			if (len === 0) return hash.toString(16);

			for (let i = 0; i < len; i++) {
				const char = str.charCodeAt(i);
				hash = (hash << 5) - hash + char;
				hash = hash & hash; // 转换为32位整数
			}

			// 确保正整数表示
			return (hash >>> 0).toString(16).padStart(8, '0');
		};

		try {
			// 生成规范化字符串
			const normalizedString = stableStringify(params);
			// 生成哈希编码
			return generateHash(normalizedString);
		} catch (error) {
			console.error('Error generating unique hash:', error);
			return null;
		}
	}

	isObject(params: any): boolean {
		return Object.prototype.toString.call(params) === '[object Object]';
	}
	isFormData(params: any): boolean {
		return Object.prototype.toString.call(params) === '[object FormData]';
	}

	onload(callback: () => void) {
		if (document.readyState === 'complete') {
			callback();
		} else {
			const handler = () => {
				callback();
				window.removeEventListener('load', handler);
			};
			window.addEventListener('load', handler);
		}
	}

	patchHistoryMethod(type: keyof History) {
		const orig = history[type];
		const _this = this;
		return function (this: unknown) {
			_this.queueFlush(() => {
				if (_this.isBeforeunload) return;
				const e = new Event(type);
				window.dispatchEvent(e);
			});
			return orig.apply(this, arguments);
		};
	}

	// 路由方法重写
	rewriteHistoryMethods() {
		// fix: vue路由在push会先触发replaceState在pushState
		// 修改history.pushState方法
		history.pushState = this.patchHistoryMethod('pushState');
		history.replaceState = this.patchHistoryMethod('replaceState');
	}

	// 路由变化监听
	routerChangeListener(handler: (e: Event) => void) {
		// 监听Hash、History路由变化
		window.addEventListener('popstate', handler, true);
		window.addEventListener('replaceState', handler, true);
		window.addEventListener('pushState', handler, true);
	}
	beforeunloadFix() {
		// fix: vue路由会监听beforeunload事件，在刷新页面的时候会通过beforeunload事件，强制触发replaceState
		window.addEventListener('beforeunload', () => {
			this.isBeforeunload = true;
		});
	}

	getBrowserInfo() {
		const userAgent = window.navigator.userAgent;
		const browserData = Bowser.parse(userAgent);
		const parserData = uaParser();
		return {
			browserName: browserData.browser.name || parserData.browser.name, // 浏览器名
			browserVersion: browserData.browser.version || parserData.browser.version, // 浏览器版本号
			osName: browserData.os.name || parserData.os.name, // 操作系统名
			osVersion: parserData.os.version || browserData.os.version, // 操作系统版本号
			deviceType: browserData.platform.type || parserData.device.type, // 设备类型
			deviceVendor:
				browserData.platform.vendor || parserData.device.vendor || '', // 设备所属公司
			deviceModel: browserData.platform.model || parserData.device.model || '', // 设备型号
			engineName: browserData.engine.name || parserData.engine.name, // 内核engine名
			engineVersion: browserData.engine.version || parserData.engine.version, // 内核engine版本号
		};
	}
	trackerParamsFormat(params: ReportParams) {
		let p = '';
		Object.keys(params).forEach(key => {
			p += `${key}=${params[key]}&`;
		});
		return p.slice(0, -1);
	}

	formatError(errObj: any) {
		let sourceURL = errObj.sourceURL || errObj.fileName; // Safari Firefox
		let line = errObj.line || errObj.lineNumber; // Safari Firefox
		let column = errObj.column || errObj.columnNumber; // Safari Firefox
		const message = errObj.message;
		// 获取堆栈信息
		const { stack } = errObj;
		if (stack) {
			const matchUrl = stack.match(/https?:\/\/[^\n]+/); // ['http://127.0.0.1/index.js:48:17',xx,xx]
			const urlFirstStack = matchUrl ? matchUrl[0] : ''; // http://127.0.0.1/index.js:48:17
			const regUrlCheck = /https?:\/\/(\S)*.js/;

			if (regUrlCheck.test(urlFirstStack)) {
				sourceURL = urlFirstStack.match(regUrlCheck)[0]; // http://127.0.0.1/index.js
			}

			const posStack = urlFirstStack.match(/:(\d+):(\d+)/);
			if (posStack && posStack.length >= 3) {
				let stackCol;
				let stackRow;
				// eslint-disable-next-line prefer-const
				[, stackCol, stackRow] = posStack; // [':48:17', '48', '17']
				if (stackCol) {
					line = stackCol;
				}
				if (stackRow) {
					column = stackRow;
				}
			}
		}
		return {
			column,
			line,
			message,
			sourceURL,
		};
	}

	// 队列刷新
	queueFlush(flushJobs: () => void) {
		if (!this.isFlushPending) {
			this.isFlushPending = true;

			Promise.resolve()
				.then(() => {
					this.isFlushPending = false;
					flushJobs();
				})
				.catch(error => {
					this.isFlushPending = false;
					this.log('warn', `Error in queue flush: ${error.message}`);
				});
		}
	}
}

export default new Utils();
